home *** CD-ROM | disk | FTP | other *** search
- {>>>> KERMIT.TEXT}
- program kermit;
- {UCSD Pascal KERMIT for the Terak p-System, from Kate MacGregor, Cornell U}
- {Adapted to Pascal Microengine by Tim Shimeall, UCI}
- {Changes:
- - Added device declarations copied from Microengine hardware documentation
- - Replaced external assembly language routines with Pascal versions
- - Modified debug messages to be label values printed
- - Changed format of packetwrite display to show header fields
- - Implemented machine-dependent packet timeout
- - Added debug packetwrites in recsw
- - Added wrap-around debug info region
- - Added legality check in showparms
- - Removed lf elimination check in echo procedure
- - Unitwrite calls replaced by calls to device driving routines
- - Most uses of char_int_rec replaced by ord and chr
- - Removed queue (no interrupts)
- - Used sets for integer ops to getaround Microengine bug
- - Changed parser from a unit to a segment procedure to allow swapping
- - Split utility procs into separate files for editing and transfer convinience
-
- }
- (*$R-*) (* turn range checking off *)
- (*$S+*) (* turn swapping on *)
- (* $L+*) (* no listing *)
-
- const blksize = 512;
- oport = 8; (* output port # *)
- (* clearscreen = 12; charcter which erases screen *)
- bell = 7; (* ASCII bell *)
- esc = 27; (* ASCII escape *)
- maxpack = 93; (* maximum packet size minus 1 *)
- soh = 1; (* start of header *)
- sp = 32; (* ASCII space *)
- cr = 13; (* ASCII CR *)
- lf = 10; (* ASCII line feed *)
- dle = 16; (* ASCII DLE (space compression prefix for psystem) *)
- del = 127; (* delete *)
- my_esc = 29; (* default esc char for connect (^]) *)
- maxtry = 5; (* number of times to retry sending packet *)
- my_quote = '#'; (* quote character I'll use *)
- my_pad = 0; (* number of padding chars I need *)
- my_pchar = 0; (* padding character I need *)
- my_eol = 13; (* end of line character i need *)
- my_time = 5; (* seconds after which I should be timed out *)
- maxtim = 20; (* maximum timeout interval *)
- mintim = 2; (* minimum time out interval *)
- at_eof = -1; (* value to return if at eof *)
- eoln_sym = 13; (* pascal eoln sym *)
- back_space = 8; (* pascal backspace sym *)
-
- (* MICROENGINE dependent constants *)
- intsize = 15; (* number of bits in an integer minus 1 *)
- Channel0=-992; {FC20 = serial Port B register}
- Channel1=-1008; {FC10 = serial Port A register}
- (* Elements of the status vector in the "StatCmdRec" declared below*)
- RegEmpty=0;
- DataReceived=1;
- OverError=2;
- FrameError=4;
- (* bits 3,5,6,and 7 are not used, since they rely on specific wiring,
- and seem to be unreliable *)
-
- (* screen control information *)
- (* console line on which to put specified info *)
- title_line = 1;
- statusline = 2;
- packet_line = 3;
- retry_line = 4;
- file_line = 5;
- error_line = 6;
- prompt_line = 7;
- debug_line = 9;
- debug_max = 12; (* Max lines of debug to show at once *)
- (* position on line to put info *)
- statuspos = 70;
- packet_pos = 19;
- retry_pos = 17;
- file_pos = 11;
-
- type packettype = packed array[0..maxpack] of char;
- parity_type = (evenpar, oddpar, markpar, spacepar, nopar);
-
- char_int_rec = record (* allows character to be treated as integer... *)
- (* is system dependent *)
- case boolean of
- true: (i: integer);
- false: (ch: char)
- end; (* record *)
-
- int_bool_rec = record (* allows integer to be treated as boolean... *)
- (* used for numeric AND,OR,XOR...system dependent *)
- (* replaced by set version to escape microengine
- bug *)
- case boolean of
- true: (i: integer);
- false: (b: set of 0..intsize);
- end; (* record *)
-
- (* MICROENGINE Dependent Types *)
- Port = (Terminal,Modem);
- Statcmdrec = RECORD CASE BOOLEAN OF (* Only the Status field is used
- in this code, but the declaration
- is from Western Digital doc. *)
- TRUE:(Command:INTEGER);
- FALSE:(Status:PACKED ARRAY [0:7] OF BOOLEAN);
- END;
- SerialRec = RECORD
- SerData:INTEGER;
- StatSynDle:StatCmdRec;
- Control2:INTEGER;
- Control1:INTEGER;
- filler:ARRAY [0..3] OF INTEGER;
- Switch:StatCmdRec;
- END;
-
- (* Parser Types *)
- statustype = (null, at_eol, unconfirmed, parm_expected, ambiguous,
- unrec, fn_expected, ch_expected);
- vocab = (nullsym, allsym, consym, debugsym, escsym, evensym, exitsym,
- filewarnsym,helpsym, ibmsym, localsym, marksym, nonesym,
- oddsym, offsym, onsym, paritysym, quitsym, recsym, sendsym,
- setsym, showsym, spacesym);
-
- var state: char; (* current state *)
- f: file of char; (* file to be received *)
- oldf: file; (* file to be sent *)
- s: string;
- eol, quote, esc_char: char;
- fwarn, ibm, half_duplex, debug: boolean;
- i, size, rpsiz, spsiz, pad, n, num_try, oldtry, timint: integer;
- recpkt, packet: packettype;
- padchar, ch: char;
- debf: text; (* file for debug output *)
- debnext:0..7; (* offset for next debug message *)
- parity: parity_type;
- xon: char;
- filebuf: packed array[1..1024] of char;
- bufpos, bufend: integer;
- parity_array: packed array[char] of char;
- ctlset: set of char;
- rec_ok, send_ok: boolean;
-
- (* MICROENGINE Dependent Variable declarations *)
- PortA,PortB:RECORD CASE BOOLEAN OF
- TRUE:(DevAdd:INTEGER);
- FALSE:(Serial:^SerialRec);
- END;
- (* Parser vars *)
-
- noun, verb, adj: vocab;
- status: statustype;
- vocablist: array[vocab] of string[13];
- filename, line: string;
- newescchar: char;
- expected: set of vocab;
-
- function read_ch(p: port; var ch: char): boolean;
- forward;
-
- function aand(x,y: integer): integer;
- forward;
-
- function aor(x,y: integer): integer;
- forward;
-
- function xor(x,y: integer): integer;
- forward;
-
- procedure error(p: packettype; len: integer);
- forward;
-
- procedure io_error(i: integer);
- forward;
-
- procedure debugwrite(s: string);
- forward;
-
- procedure debugint(s: string; i: integer);
- forward;
-
- procedure writescreen(s: string);
- forward;
-
- procedure refresh_screen(numtry, num: integer);
- forward;
-
- function min(x,y: integer): integer;
- forward;
-
- function tochar(ch: char): char;
- forward;
-
- function unchar(ch: char): char;
- forward;
-
- function ctl(ch: char): char;
- forward;
-
- function getfil(filename: string): boolean;
- forward;
-
- procedure bufemp(buffer: packettype; var f: text; len: integer);
- forward;
-
- function bufill(var buffer: packettype): integer;
- forward;
-
- procedure spar(var packet: packettype);
- forward;
-
- procedure rpar(var packet: packettype);
- forward;
-
- procedure spack(ptype: char; num:integer; len: integer; data: packettype);
- forward;
-
- function getch(var r: char; p: port): boolean;
- forward;
-
- function getsoh(p: port): boolean;
- forward;
-
- function rpack(var len, num: integer; var data: packettype): char;
- forward;
-
- procedure read_str(p: port; var s: string);
- forward;
-
- procedure packetwrite(p: packettype; len: integer);
- forward;
-
- procedure show_parms;
- forward;
-
- procedure uppercase(var s: string); forward;
-
-
- (*$I WDFORW.TEXT *) (* Forward Declarations for WDPROCS.TEXT *)
- (*$I HELP.TEXT*) (* Segment Procedure Help *)
- (*$I SENDSW.TEXT*) (* Segment Procedure Sendsw *)
- (*$I RECSW.TEXT*) (* Segment Procedure Recsw *)
- (*$I PARSE.TEXT*) (* Segment Function Parse *)
- (*$I WDPROCS.TEXT*) (* MICROENGINE dependent routines*)
- (*$I UTILS.TEXT *) (* General Utility procedures *)
- (*$I RSUTILS.TEXT *) (* Utility procedures for send and receive *)
-
- procedure connect;
-
- (* connect to remote host (terminal emulation *)
-
- var ch: char;
- close: boolean;
-
- procedure read_esc;
-
- (* read charcter after esc char and interpret it *)
-
- begin
- repeat
- until read_ch(terminal,ch); (* wait until they've typed something in *)
- if (ch in ['a'..'z']) then (* uppercase it *)
- ch := chr(ord(ch) - ord('a') + ord('A'));
- if ch in [{'B',}'C','S','?'] then
- case ch of
- (*'B': sendbrk; B: send a break to the IBM *)
- 'C': close := true; (* C: end connection *)
- 'S': begin (* S: show status *)
- noun := allsym;
- showparms
- end; (* S *)
- '?': begin (* ?: show options *)
- (* writeln('B Send a BREAK signal.'); *)
- write('C Close Connection, return to ');
- writeln('KERMIT-UCSD command level.');
- writeln('S Show Status of connection');
- writeln('? Print this list');
- write('^',esc_char,' send the escape ');
- writeln('character itself to the');
- writeln(' remote host.')
- end; (* ? *)
- end (* case *)
- else if ch = esc_char then (* ESC-char: send it out *)
- begin
- if half_duplex then
- begin
- echo(ch);
- while not istbtr do;
- sndbbt(ch);
- end (* if *)
- end (* else if *)
- else (* anything else: ignore *)
- write(chr(bell))
- end; (* read_esc *)
-
- begin (* connect *)
- writeln('Connecting to host...type CTRL-',ctl(esc_char),' C to exit');
- close := false;
- repeat
- if read_ch(modem,ch) then (* if char from host then *)
- echo(ch); (* echo it *)
-
- if read_ch(terminal,ch) then (* if char from keyboard then *)
- if ch <> esc_char then (* if not ESC-char then *)
- begin
- if half_duplex then (* echo it if half-duplex *)
- echo(ch);
- while not istbtr do;
- sndbbt(ch) (* send it out the port *)
- end (* if *)
- else (* ch = esc_char *) (* else is ESC-char so *)
- read_esc; (* interpret next char *)
- until close; (* if still connected, get more *)
- writeln('Disconnected')
- end; (* connect *)
-
- procedure fill_parity_array;
-
- (* parity value table for even parity...not(entry) = odd parity *)
-
- const min = 0;
- max = 126;
-
- var i, shifter, counter: integer;
- minch, maxch, ch: char;
- r: char_int_rec;
-
- begin
- minch := chr(min);
- maxch := chr(max);
- case parity of
- evenpar:
- begin
- for ch := minch to maxch do
- begin
- r.ch := ch; (* put char into variant record *)
- shifter := aand(r.i,255); (* mask off parity bit *)
- counter := 0;
- for i := 1 to 7 do (* count the 1's *)
- begin
- if odd(shifter) then
- counter := counter + 1;
- shifter := shifter div 2
- end; (* for i *)
- if odd(counter) then (* stick a 1 on if necessary *)
- parity_array[ch] := chr(aor(ord(ch),128))
- else
- parity_array[ch] := chr(aand(ord(ch),127))
- end; (* for ch *)
- end; (* case even *)
- oddpar:
- begin
- for ch := minch to maxch do
- begin
- r.ch := ch; (* put char into variant record *)
- shifter := aand(r.i,255); (* mask off parity bit *)
- counter := 0;
- for i := 1 to 7 do (* count the 1's *)
- begin
- if odd(shifter) then
- counter := counter + 1;
- shifter := shifter div 2
- end; (* for i *)
- if odd(counter) then (* stick a 1 on if necessary *)
- parity_array[ch] := chr(aand(ord(ch),127))
- else
- parity_array[ch] := chr(aor(ord(ch),128))
- end; (* for ch *)
- end; (* case odd *)
- markpar:
- for ch := minch to maxch do (* stick a 1 on all chars *)
- parity_array[ch] := chr(aor(ord(ch),128));
- spacepar:
- for ch := minch to maxch do (* mask off parity on all chars *)
- parity_array[ch] := chr(aand(ord(ch),127));
- nopar:
- for ch := minch to maxch do (* don't mess w/parity bit at all *)
- parity_array[ch] := ch;
- end; (* case *)
- end; (* fill_parity_array *)
-
- procedure write_bool(s: string; b: boolean);
-
- (* writes message & 'on' if b, 'off' if not b *)
- begin
- write(s);
- case b of
- true: writeln('on');
- false: writeln('off');
- end; (* case *)
- end; (* write_bool *)
-
- procedure show_parms;
-
- (* shows the various settable parameters *)
-
- begin
- if noun in [allsym, debugsym, ibmsym, escsym, filewarnsym, localsym,
- paritysym] then
- case noun of
- allsym:
- begin
- write_bool('Debugging is ',debug);
- writeln('Escape character is ^',ctl(esc_char));
- write_bool('File warning is ',fwarn);
- write_bool('IBM is ',ibm);
- write_bool('Local echo is ',halfduplex);
- case parity of
- evenpar: write('Even');
- markpar: write('Mark');
- nopar: write('No');
- oddpar: write('Odd');
- spacepar: write('Space');
- end; (* case *)
- writeln(' parity');
- end; (* allsym *)
- debugsym: write_bool('Debugging is ',debug);
- escsym: writeln('Escape character is ^',ctl(esc_char));
- filewarnsym: write_bool('File warning is ',fwarn);
- ibmsym: write_bool('IBM is ',ibm);
- localsym: write_bool('Local echo is ',halfduplex);
- paritysym: begin
- case parity of
- evenpar: write('Even');
- markpar: write('Mark');
- nopar: write('No');
- oddpar: write('Odd');
- (' parity');
- end; (* paritysym *)
- end (* case *)
- else write(chr(bell));
- end; (* show_sym *)
-
- procedure set_parms;
-
- (* sets the parameters *)
-
- begin
- case noun of
- debugsym: case adj of
- onsym: begin
- debug := true;
- (*$I-*)
- rewrite(debf,'CONSOLE:')
- (*I+*)
- end; (* onsym *)
- offsym: debug := false
- end; (* case adj *)
- escsym: escchar := newescchar;
- filewarnsym: fwarn := (adj = onsym);
- ibmsym: case adj of
- onsym: begin
- ibm := true;
- parity := markpar;
- half_duplex := true;
- fillparityarray
- end; (* onsym *)
- offsym: begin
- ibm := false;
- parity := nopar;
- half_duplex := false;
- fillparityarray
- end; (* onsym *)
- end; (* case adj *)
- localsym: halfduplex := (adj = onsym);
- paritysym: begin
- case adj of
- evensym: parity := evenpar;
- marksym: parity := markpar;
- nonesym: parity := nopar;
- oddsym: parity := oddpar;
- spacesym: parity := spacepar;
- end; (* case *)
- fill_parity_array;
- end; (* paritysym *)
- end; (* case *)
- end; (* set_parms *)
-
- procedure initialize;
-
- var ch: char;
-
- begin
- pad := mypad;
- padchar := chr(mypchar);
- eol := chr(my_eol);
- esc_char := chr(my_esc);
- quote := my_quote;
- ctlset := [chr(1)..chr(31),chr(del),quote];
- half_duplex := false;
- debug := false;
- debnext:=0;
- fwarn := false;
- spsiz := max_pack;
- rpsiz := max_pack;
- n := 0;
- parity := nopar;
- initvocab;
- fill_parity_array;
- ibm := false;
- xon := chr(17);
- bufpos := 1;
- bufend := 0;
- init;
- end; (* initialize *)
-
- procedure closeup;
-
- begin
- finit;
- writeln(chr(esc),'E'{clearscreen});
- end; (* closeup *)
-
- begin (* kermit *)
- initialize;
- repeat
- write('Kermit-UCSD> ');
- readstr(terminal,line);
- case parse of
- unconfirmed: writeln('Unconfirmed');
- parm_expected: writeln('Parameter expected');
- ambiguous: writeln('Ambiguous');
- unrec: writeln('Unrecognized command');
- fn_expected: writeln('File name expected');
- ch_expected: writeln('Single character expected');
- null: case verb of
- consym: connect;
- helpsym: help;
- recsym: begin
- recsw(rec_ok);
- gotoxy(0,debugline);
- write(chr(bell));
- if rec_ok then
- writeln('successful receive')
- else
- writeln('unsuccessful receive');
- (*$I-*) (* set i/o checking off *)
- close(oldf);
- (*$I+*) (* set i/o checking back on *)
- gotoxy(0,promptline);
- end; (* recsym *)
- sendsym: begin
- uppercase(filename);
- sendsw(send_ok);
- gotoxy(0,debugline);
- write(chr(bell));
- if send_ok then
- writeln('successful send')
- else
- writeln('unsuccessful send');
- (*$I-*) (* set i/o checking off *)
- close(oldf);
- (*$I+*) (* set i/o checking back on *)
- gotoxy(0,promptline);
- end; (* sendsym *)
- setsym: set_parms;
- show_sym: show_parms;
- end; (* case verb *)
- end; (* case parse *)
- unitclear(1); (* clear any trash in input *)
- unitclear(2);
- until (verb = exitsym) or (verb = quitsym);
- closeup
- end. (* kermit *)
-
- {>>>>WDFORW.TEXT}
- procedure INIT; forward;
- function ISTARR:boolean ; forward;
- function ISTBRR:boolean; forward;
- function ISTAOR:boolean ; forward;
- function ISTBOR:boolean ; forward;
- function ISTAFE:boolean ; forward;
- function ISTBFE:boolean; forward;
- function ISTATR:boolean ; forward;
- function ISTBTR :boolean; forward;
- function RCVABT:CHAR ; forward;
- function RCVBBT:CHAR ; forward;
- procedure SNDABT (BT:CHAR); forward;
- procedure SNDBBT (BT:CHAR); forward;
- procedure FINIT; forward;
-
- {>>>> HELP.TEXT}
- segment procedure help;
- {UCSD Pascal KERMIT for the Terak p-System, from Kate MacGregor, Cornell U}
-
- procedure keypress;
-
- var ch: char;
-
- begin
- writeln('---------------Press any key to continue---------------');
- repeat
- until readch(terminal,ch);
- writeln(chr(esc),'E'{clearscreen})
- end; (* keypress *)
-
- procedure help1;
-
- var ch: char;
-
- begin
- if (noun = nullsym) then
- begin
- writeln('KERMIT is a family of programs that do reliable file transfer');
- write('between computers over TTY lines. KERMIT can also be ');
- writeln('used to make the ');
- writeln('microcomputer behave as a terminal for a mainframe. These are the ');
- writeln('commands for theUCSD p-system version, KERMIT-UCSD:');
- writeln
- end; (* if *)
- if (noun = nullsym) or (noun = consym) then
- begin
- writeln(' CONNECT To make a "virutual terminal" connection to a remote');
- writeln(' system.');
- writeln;
- write(' To break the connection and "escape" back to the micro,');
- writeln;
- writeln(' type the escape sequence (CTRL-] C, that is Control ');
- writeln(' rightbracket followed immediately by the letter C.)');
- writeln;
- end; (* if *)
- if (noun = nullsym) or (noun = exitsym) then
- begin
- writeln(' EXIT To return back to main command level of the p-system.');
- writeln;
- end; (* if *)
- if (noun = nullsym) or (noun = helpsym) then
- begin
- writeln(' HELP To get a list of KERMIT commands.');
- writeln;
- end; (* if *)
- if (noun = nullsym) or (noun = quitsym) then
- begin
- writeln(' QUIT Same as EXIT.');
- writeln;
- end; (* if *)
- if (noun = nullsym) or (noun = recsym) then
- begin
- writeln(' RECEIVE To accept a file from the remote system.');
- writeln;
- end; (* if *)
- end; (* help1 *)
-
- procedure help2;
-
- var ch: char;
-
- begin
- if (noun = nullsym) or (noun = sendsym) then
- begin
- writeln(' SEND To send a file or group of files to the remote system.');
- writeln;
- end; (* if *)
- if (noun = nullsym) then
- keypress;
- if (noun = nullsym) or (noun = setsym) then
- begin
- writeln(' SET To establish system-dependent parameters. The ');
- writeln(' SET options are as follows: ');
- writeln;
- if (adj = nullsym) or (adj = debugsym) then
- begin
- writeln(' DEBUG To set debug mode ON or OFF ');
- writeln(' (default is OFF).');
- writeln;
- end; (* if *)
- if (adj = nullsym) or (adj = escsym) then
- begin
- writeln(' ESCAPE To change the escape sequence that ');
- writeln(' lets you return to the PC Kermit from');
- write(' the remote host.');
- writeln(' The default is CTRL-] c.');
- writeln;
- end; (* if *)
- if (adj = nullsym) or (adj = filewarnsym) then
- begin
- writeln(' FILE-WARNING ON/OFF, default is OFF. If ON, ');
- writeln(' Kermit will warn you and rename an ');
- writeln(' incoming file so as not to write over');
- writeln(' a file that currently exists with the');
- writeln(' same name');
- writeln;
- end; (* if *)
- if (adj = nullsym) then
- keypress;
- end; (* if *)
- end; (* help2 *)
-
- procedure help3;
-
- begin
- if (noun = nullsym) or (noun = setsym) then
- begin
- if (adj = nullsym) or (adj = ibmsym) then
- begin
- writeln(' IBM ON/OFF, default is OFF. This flag ');
- write(' should be ON only when ');
- writeln('transfering files');
- writeln(' between the micro and an IBM VM/CMS');
- writeln(' system. It also causes the parity to');
- write(' be set appropriately ');
- writeln('(mark) and activates');
- writeln(' local echoing');
- writeln;
- end; (* if *)
- if (adj = nullsym) or (adj = localsym) then
- begin
- write(' LOCAL-ECHO ON/OFF, default is OFF. This sets the');
- writeln;
- writeln(' duplex. It should be ON when using ');
- writeln(' the IBM and OFF for the DEC-20.');
- writeln;
- end; (* if *)
- end; (* if *)
- end; (* help3 *)
-
- procedure help4;
-
- begin
- if (noun = setsym) or (noun = nullsym) then
- begin
- if (adj = nullsym) or (adj = paritysym) then
- begin
- writeln(' PARITY EVEN, ODD, MARK, SPACE, or NONE.');
- writeln(' NONE is the default but if the IBM ');
- writeln(' flag is set, parity is set to MARK. ');
- writeln(' This flag selects the parity for ');
- write(' outgoing and incoming characters during');
- writeln;
- write(' CONNECT and file transfer to match the');
- writeln;
- writeln(' requirements of the host.');
- writeln;
- end; (* if *)
- end; (* if *)
- if (noun = nullsym) or (noun = showsym) then
- begin
- writeln(' SHOW To see the values of parameters that can be modified');
- writeln(' via the SET command. Options are the same as for SET,');
- writeln(' except that a SHOW ALL command has been added.');
- end; (* if *)
- end; (* help4 *)
-
- begin
- help1;
- help2;
- help3;
- help4
- end; (* help *)
-
- {>>>> SENDSW.TEXT}
-
- (* Send Section *)
- {UCSD Pascal KERMIT for the Terak p-System, from Kate MacGregor, Cornell U}
-
- segment procedure sendsw(var send_ok: boolean);
-
- var io_status: integer;
-
- procedure openfile;
-
- (* resets file & gets past first 2 blocks *)
-
- begin
- (*$I-*) (* turn off compiler i/o checking temporarily *)
- reset(oldf,filename);
- (*$I+*) (* turn compiler i/o checking back on *)
- io_status := io_result;
- if (iostatus = 0) then
- if (pos('.TEXT',filename) = length(filename) - 4) then
- (* is a text file, so *)
- i := blockread(oldf,filebuf,2); (* skip past 2 block header *)
- end; (* openfile *)
-
- function sinit: char;
-
- (* send init packet & receive other side's *)
-
- var num, len, i: integer; (* packet number and length *)
- ch: char;
-
- begin
- if debug then
- debugwrite('sinit');
-
- if numtry > maxtry then
- begin
- sinit := 'a';
- exit(sinit)
- end;
-
- num_try := num_try + 1;
- spar(packet);
-
- if istbrr then ch:=rcvbbt; (* clear modem buffer *)
-
- refresh_screen(numtry,n);
-
- spack('S',n mod 64,6,packet);
-
- ch := rpack(len,num,recpkt);
-
- if (ch = 'N') then
- begin
- sinit := 's';
- exit(sinit)
- end (* if 'N' *)
- else if (ch = 'Y') then
- begin
- if ((n mod 64) <> num) then (* not the right ack *)
- begin
- sinit := state;
- exit(sinit)
- end;
- rpar(recpkt);
- if (eol = chr(0)) then (* if they didn't spec eol *)
- eol := chr(my_eol); (* use mine *)
- if (quote = chr(0)) then (* if they didn't spec quote *)
- quote := my_quote; (* use mine *)
- ctl_set := [chr(1)..chr(31),chr(del),quote];
- numtry := 0;
- n := n + 1; (* increase packet number *)
- sinit := 'f';
- exit(sinit)
- end (* else if 'Y' *)
- else if (ch = 'E') then
- begin
- error(recpkt,len);
- sinit := 'a'
- end (* if 'E' *)
- else if (ch = chr(0)) then
- sinit := state
- else if (ch <> 'N') then
- sinit := 'a'
- end; (* sinit *)
-
- function sdata: char;
-
- (* send file data *)
-
- var num, len: integer;
- ch: char;
- packarray: array[false..true] of packettype;
- sizearray: array[false..true] of integer;
- current: boolean;
- b: boolean;
-
- function other(b: boolean): boolean;
-
- (* complements a boolean which is used as array index *)
-
- begin
- if b then
- other := false
- else
- other := true
- end; (* other *)
-
- begin
- current := true;
- packarray[current] := packet;
- sizearray[current] := size;
- while (state = 'd') do
- begin
- if (numtry > maxtry) then (* if too many tries, give up *)
- state := 'a';
-
- b := other(current);
- numtry := numtry + 1;
-
- (* send a data packet *)
- spack('D',n mod 64,sizearray[current],packarray[current]);
-
- refresh_screen(numtry,n);
- (* set up next packet *)
- sizearray[b] := bufill(packarray[b]);
-
- ch := rpack(len,num,recpkt); (* receive a packet *)
- if ch = 'N' then (* NAK, so just stay in this state *)
- if ((n+1) mod 64 <> num) then (* unless NAK for next, which *)
- sdata := state
- else (* is just like ACK for this packet *)
- begin
- if num > 0 then
- num := (num - 1) (* in which case, decrement num *)
- else
- num := 63;
- ch := 'Y'; (* and indicate an ACK *)
- end; (* else *)
-
- if (ch = 'Y') then
- begin
- if ((n mod 64) <> num) then (* if wrong ACK *)
- begin
- sdata := state; (* stay in same state *)
- exit(sdata); (* get out of here *)
- end; (* if *)
- if numtry > 1 then (* if anything in buffer, flush it *)
- if istbrr then begin
- ch:=rcvbbt;
- ch:='Y';
- end;
- numtry := 0;
- n := n + 1;
- current := b;
- if sizearray[current] = ateof then
- state := 'z' (* set state to eof *)
- else
- state := 'd' (* else stay in data state *)
- end (* if *)
- else if (ch = 'E') then
- begin
- error(recpkt,len);
- state := 'a'
- end (* if 'E' *)
- else if (ch = chr(0)) then (* receive failure, so stay in d *)
- begin
- end
- else if (ch <> 'N') then
- state := 'a' (* on any other goto abort state *)
- end; (* while *)
- size := sizearray[current];
- packet := packarray[current];
- sdata := state
- end; (* sdata *)
-
- function sfile: char;
-
- (* send file header *)
-
- var num, len, i: integer;
- ch: char;
- fn: packettype;
- oldfn: string;
-
- procedure legalize(var fn: string);
-
- (* make sure file name will be legal to other computer *)
-
- var count, i, j, l: integer;
-
- procedure uppercase(var s: string);
-
- var i: integer;
-
- begin
- for i := 1 to length(s) do
- if s[i] in ['a'..'z'] then
- s[i] := chr(ord('A') + ord(s[i]) - ord('a'))
- end; (* uppercase *)
-
- begin
- count := 0;
- l := length(fn);
- for i := 1 to l do (* count '.'s in fn *)
- if fn[i] = '.' then
- count := count + 1;
- for i := 1 to count-1 do (* remove all but 1 *)
- begin
- j := 1;
- while (j < l) and (fn[j] <> '.') do
- j := j + 1;
- delete(fn,j,1);l := l - 1
- end; (* for i *)
- l := length(fn);
- i := pos(':',fn);
- if (i <> 0) then
- begin
- fn := copy(fn,i,l-i);
- l := length(fn)
- end;
- i := 1;
- while (i <= length(fn)) do
- if not(fn[i] in ['a'..'z','A'..'Z','.','0'..'9']) then
- delete(fn,i,1)
- else
- i := i + 1;
- uppercase(fn)
- end; (* legalize *)
-
- begin
- if debug then
- debugwrite('sfile');
-
- if (numtry > maxtry) then (* if too many tries, give up *)
- begin
- sfile := 'a';
- exit(sfile)
- end;
- numtry := numtry + 1;
-
- oldfn := filename;
- legalize(filename); (* make filename acceptable to remote *)
- len := length(filename);
-
- moveleft(filename[1],fn[0],len); (* move filename into a packettype *)
-
- gotoxy(filepos,fileline);
- write(oldfn,' ==> ',filename);
-
- refresh_screen(numtry,n);
-
- spack('F',n mod 64,len,fn); (* send file header packet *)
-
- size := bufill(packet); (* get first data from file *)
- (* while waiting for response *)
-
- ch := rpack(len,num,recpkt);
- if ch = 'N' then (* NAK, so just stay in this state *)
- if ((n+1) mod 64 <> num) then (* unless NAK for next packet, which *)
- exit(sfile) (* is just like ACK for this packet *)
- else
- begin
- if (num > 0) then
- num := (num - 1) (* in which case, decrement num *)
- else
- num := 63;
- ch := 'Y'; (* and indicate an ACK *)
- end; (* else *)
-
- if (ch = 'Y') then
- begin
- if ((n mod 64) <> num) then (* if wrong ACK, stay in F state *)
- exit(sfile);
- numtry := 0;
- n := n + 1;
- sfile := 'd';
- end (* if *)
- else if (ch = 'E') then
- begin
- error(recpkt,len);
- sfile := 'a'
- end (* if 'E' *)
- else if (ch <> chr(0)) and (ch <> 'N') then (* don't recognize it *)
- sfile := 'a'
- end; (* sfile *)
-
- function seof: char;
-
- (* send end of file *)
-
- var num, len: integer;
- ch: char;
-
- begin
- if debug then
- debugwrite('seof');
-
- if (numtry > maxtry) then (* if too many tries, give up *)
- begin
- seof := 'a';
- exit(seof)
- end;
- numtry := numtry + 1;
-
- refresh_screen(numtry,n);
-
- spack('Z',(n mod 64),0,packet); (* send end of file packet *)
-
- if debug then
- debugwrite('seof1');
-
- ch := rpack(len,num,recpkt);
- if ch = 'N' then (* NAK, so just stay in this state *)
- if ((n+1) mod 64 <> num) then (* unless NAK for next packet, which *)
- exit(seof) (* is just like ACK for this packet *)
- else
- begin
- if num > 0 then
- num := (num - 1) (* in which case, decrement num *)
- else
- num := 63;
- ch := 'Y'; (* and indicate an ACK *)
- end; (* else *)
-
- if (ch = 'Y') then
- begin
- if debug then
- debugwrite('seof2');
- if ((n mod 64) <> num) then (* if wrong ACK, stay in F state *)
- exit(seof);
- numtry := 0;
- n := n + 1;
- if debug then
- debugwrite(concat('closing ',s));
- close(oldf);
- seof := 'b'
- end (* if *)
- else if (ch = 'E') then
- begin
- error(recpkt,len);
- seof := 'a'
- end (* if 'E' *)
- else if (ch = chr(0)) then (* receive failed, so stay in z state *)
- begin
- end
- else if (ch <> 'N') then (* other error, just abort *)
- seof := 'a'
- end; (* seof *)
-
- function sbreak: char;
-
- var num, len: integer;
- ch: char;
-
- (* send break (end of transmission) *)
-
- begin
- if debug then
- debugwrite('sbreak');
-
- if (numtry > maxtry) then (* if too many tries, give up *)
- begin
- sbreak := 'a';
- exit(sbreak)
- end;
- numtry := numtry + 1;
-
- refresh_screen(numtry,n);
-
- spack('B',(n mod 64),0,packet); (* send end of file packet *)
-
- ch := rpack(len,num,recpkt);
- if ch = 'N' then (* NAK, so just stay in this state *)
- if ((n+1) mod 64 <> num) then (* unless NAK for next packet, which *)
- exit(sbreak) (* is just like ACK for this packet *)
- else
- begin
- if num > 0 then
- num := (num - 1) (* in which case, decrement num *)
- else
- num := 63;
- ch := 'Y'; (* and indicate an ACK *)
- end; (* else *)
-
- if (ch = 'Y') then
- begin
- if ((n mod 64) <> num) then (* if wrong ACK, stay in B state *)
- exit(sbreak);
- numtry := 0;
- n := n + 1;
- sbreak := 'c' (* else, switch state to complete *)
- end (* if *)
- else if (ch = 'E') then
- begin
- error(recpkt,len);
- sbreak := 'a'
- end (* if 'E' *)
- else if (ch = chr(0)) then (* receive failed, so stay in z state *)
- begin
- end
- else if (ch <> 'N') then (* other error, just abort *)
- sbreak := 'a'
- end; (* sbreak *)
-
- (* state table switcher for sending *)
-
- begin (* sendsw *)
-
- if debug then
- debugwrite(concat('Opening ',filename));
-
- openfile;
- if io_status <> 0 then
- begin
- writeln(chr(esc),'E'{clear_screen});
- io_error(io_status);
- send_ok := false;
- exit(sendsw)
- end;
-
- write_screen('Sending');
- state := 's';
- n := 0; (* set packet # *)
- numtry := 0;
- while true do
- if state in ['d', 'f', 'z', 's', 'b', 'c', 'a'] then
- case state of
- 'd': state := sdata;
- 'f': state := sfile;
- 'z': state := seof;
- 's': state := sinit;
- 'b': state := sbreak;
- 'c': begin
- send_ok := true;
- exit(sendsw)
- end; (* case c *)
- 'a': begin
- send_ok := false;
- exit(sendsw)
- end (* case a *)
- end (* case *)
- else (* state not in legal states *)
- begin
- send_ok := false;
- exit(sendsw)
- end (* else *)
- end; (* sendsw *)
-
- {>>>> RECSW.TEXT}
-
- (* RECEIVE SECTION *)
- {UCSD Pascal KERMIT for the Terak p-System, from Kate MacGregor, Cornell U}
-
- segment procedure recsw(var rec_ok: boolean);
-
- function rdata: char;
-
- (* send file data *)
-
- var num, len: integer;
- ch: char;
-
- begin
-
- repeat
- if numtry > maxtry then
- begin
- debugwrite('too many intial retries in rdata');
- state := 'a';
- exit(rdata)
- end;
-
- num_try := num_try + 1;
-
- ch := rpack(len,num,recpkt); (* receive a packet *)
- if debug and (ch<>chr(0)) then packetwrite(recpkt,len);
-
- refresh_screen(numtry,n);
-
- if (ch = 'D') then (* got data packet *)
- begin
- if (num <> (n mod 64)) then (* wrong packet *)
- begin
- if (oldtry > maxtry) then
- begin
- debugwrite('too many data retries in rdata');
- rdata := 'a'; (* too many tries, abort *)
- exit(rdata)
- end; (* if *)
-
- n := n - 1;
-
- if (num = (n mod 64)) then (* previous packet again *)
- begin (* so re-ACK it *)
- debugint('re-acking ',num);
- spack('Y',num,6,packet);
- numtry := 0; (* reset try counter *)
- (* stay in same state *)
- end (* if *)
- else begin (* wrong number *)
- debugwrite('wrong data sequence no. in rdata');
- state := 'a' (* so abort *)
- end
- end (* if *)
- else (* right packet *)
- begin
- bufemp(recpkt,f,len); (* write data to file *)
- spack('Y',(n mod 64),0,packet); (* ACK packet *)
- oldtry := numtry; (* reset try counters *)
- if numtry > 1 then
- if istbrr then (* clear buffer *)
- begin
- ch:=rcvbbt;
- ch:='D';
- end;
- numtry := 0;
- n := n + 1 (* bump packet number *)
- (* stay in data send state *)
- end (* else *)
- end (* if 'D' *)
- else if (ch = 'F') then (* file header *)
- begin
- if (oldtry > maxtry) then
- begin
- debugwrite('too many file head tries in rdata');
- rdata := 'a'; (* too many tries, abort *)
- exit(rdata)
- end; (* if *)
-
- n := n - 1;
-
- if (num = (n mod 64)) then (* previous packet again *)
- begin (* so re-ACK it *)
- debugint('re-acking file header ',num);
- spack('Y',num,0,packet);
- if istbrr then begin
- ch:=rcvbbt; (* and empty out buffer *)
- ch:='F';
- end;
- numtry := 0; (* reset try counter *)
- state := state; (* stay in same state *)
- end (* if *)
- else begin
- debugwrite('file info not previous packet in rdata');
- state := 'a' (* not previous packet, abort *)
- end
- end (* if 'F' *)
- else if (ch = 'Z') then (* end of file *)
- begin
- if (num <> (n mod 64)) then(* wrong packet, abort *)
- begin
- debugwrite('wrong eof packet in rdata');
- rdata := 'a';
- exit(rdata)
- end; (* if *)
- spack('Y',n mod 64,0,packet); (* ok, ACK it *)
- close(f,lock); (* close up the file *)
- n := n + 1; (* bump packet counter *)
- state := 'f'; (* go to complete state *)
- end (* else if 'Z' *)
- else if (ch = 'E') then (* error packet *)
- begin
- error(recpkt,len); (* display error *)
- state := 'a' (* and abort *)
- end (* if 'E' *)
- else if (ch <> chr(0)) then begin (* some other packet type, *)
- state := 'a'; (* abort *)
- debugwrite('wierd rdata packet');
- end
- until (state <> 'd');
- rdata := state
- end; (* rdata *)
-
- function rfile: char;
-
- (* receive file header *)
-
- var num, len: integer;
- ch: char;
- oldfn: string;
- i: integer;
-
- procedure makename(recpkt: packettype; var fn: string; l: integer);
-
- function exist(fn: string): boolean;
-
- (* returns true if file named fn exists *)
-
- var f: file;
-
- begin
- (*$I-*) (* turn off i/o checking *)
- reset(f,fn);
- exist := (ioresult = 0)
- (*$I+*)
- end; (* exist *)
-
- procedure checkname(var fn: string);
-
- (* if file fn exists, makes a new name which doesn't *)
- (* does this by changing letters in file name until it *)
- (* finds some combination which doesn't exitst *)
-
- var ch: char;
- i: integer;
-
- begin
- i := 1;
- while (i <= length(fn)) and exist(fn) do
- begin
- ch := 'A';
- while (ch in ['A'..'Z']) and exist(fn) do
- begin
- fn[i] := ch;
- ch := succ(ch);
- end; (* while *)
- i := i + 1
- end; (* while *)
- end; (* checkname *)
-
- begin (* makename *)
- fn := copy(' ',1,15); (* stretch length *)
- moveleft(recpkt[0],fn[1],l); (* get filename from packet *)
- oldfn := copy(fn, 1,l); (* save fn sent to show user *)
- fn := copy(fn,1,min(15,l)); (* set length of filename *)
- (* and make sure <= 15 *)
- uppercase(fn);
- if pos('.TEXT',fn) <> length(fn)-4 then
- begin
- if length(fn) > 10 then
- fn := copy(fn,1,10); (* can only be 15 long in all *)
- fn := concat(fn,'.TEXT'); (* and we'll add .TEXT *)
- end; (* if *)
- if fwarn then (* if file warning is on *)
- checkname(fn); (* must check that name unique *)
- end; (* makename *)
-
- begin (* rfile *)
- if debug then
- debugwrite('rfile');
-
- if (numtry > maxtry) then (* if too many tries, give up *)
- begin
- rfile := 'a';
- exit(rfile)
- end;
- numtry := numtry + 1;
-
- ch := rpack(len,num,recpkt); (* receive a packet *)
- if debug and (ch<>chr(0)) then packetwrite(recpkt,len);
- refresh_screen(numtry,n);
-
- if ch = 'S' then (* send init, maybe our ACK lost *)
- begin
- if (oldtry > maxtry) then (* too many tries, abort *)
- begin
- debugwrite('too many tries in rfile init');
- rfile := 'a';
- exit(rfile)
- end; (* if *)
-
- n := n - 1;
-
- if num = (n mod 64) then (* previous packet mod 64? *)
- begin (* yes, ACK it again *)
- debugint('re-acking init ',num);
- spar(packet); (* with our send init params *)
- spack('Y',num,7,packet);
- numtry := 0; (* reset try counter *)
- rfile := state; (* stay in same state *)
- end (* if *)
- else (* not previous packet, abort *)
- state := 'a'
- end (* if 'S' *)
- else if (ch = 'Z') then (* end of file *)
- begin
- if (oldtry > maxtry) then (* too many tries, abort *)
- begin
- debugwrite('too many tries in filehead eof');
- rfile := 'a';
- exit(rfile)
- end; (* if *)
-
- n := n - 1;
-
- if num = (n mod 64) then (* previous packet mod 64? *)
- begin (* yes, ACK it again *)
- debugint('re-acking eof ',num);
- spack('Y',num,0,packet);
- numtry := 0;
- rfile := state (* stay in same state *)
- end (* if *)
- else
- rfile := 'a' (* no, abort *)
- end (* else if *)
- else if (ch = 'F') then (* file header *)
- begin (* which is what we really want *)
- if (num <> (n mod 64)) then (* if wrong packet, abort *)
- begin
- debugwrite('wrong seq. of file header');
- rfile := 'a';
- exit(rfile)
- end;
-
- makename(recpkt,filename,len); (* get filename, make unique if filew *)
- gotoxy(filepos,fileline);
- write(oldfn,' ==> ',filename);
-
- if not getfil(filename) then (* try to open new file *)
- begin
- ioerror(ioresult); (* if unsuccessful, tell them *)
- rfile := 'a'; (* and abort *)
- exit(rfile)
- end; (* if *)
-
- spack('Y',n mod 64,0,packet); (* ACK file header *)
- oldtry := numtry; (* reset try counters *)
- numtry := 0;
- n := n + 1; (* bump packet number *)
- rfile := 'd'; (* switch to data state *)
- end (* else if *)
- else if ch = 'B' then (* break transmission *)
- begin
- if (num <> (n mod 64)) then (* wrong packet, abort *)
- begin
- debugwrite('wrong sequence in break packet');
- rfile := 'a';
- exit(rfile)
- end;
- spack('Y',n mod 64,0,packet); (* say ok *)
- rfile := 'c' (* go to complete state *)
- end (* else if *)
- else if (ch = 'E') then
- begin
- error(recpkt,len);
- rfile := 'a'
- end
- else if (ch = chr(0)) then (* returned false *)
- rfile := state (* so stay in same state *)
- else begin (* some weird state, so abort *)
- rfile := 'a';
- debugwrite('wierd rfile packet');
- end
- end; (* rfile *)
-
- function rinit: char;
-
- (* receive initialization *)
-
- var num, len: integer; (* packet number and length *)
- ch: char;
-
- begin
- if debug then
- debugwrite('rinit');
-
- numtry := numtry + 1;
-
- ch := rpack(len,num,recpkt); (* receive a packet *)
- if debug and (ch<>chr(0)) then packetwrite(recpkt,len);
- refresh_screen(num_try,n);
-
- if (ch = 'S') then (* send init packet *)
- begin
- rpar(recpkt); (* get other side's init data *)
- spar(packet); (* fill packet with my init data *)
- ctl_set := [chr(1)..chr(31),chr(del),quote];
- spack('Y',n mod 64,7,packet); (* ACK with my params *)
- oldtry := numtry; (* save old try count *)
- numtry := 0; (* start a new counter *)
- n := n + 1; (* bump packet number *)
- rinit := 'f'; (* enter file send state *)
- end (* if 'S' *)
- else if (ch = 'E') then
- begin
- rinit := 'a';
- error(recpkt,len)
- end (* if 'E' *)
- else if (ch = chr(0)) then
- rinit := 'r' (* stay in same state *)
- else begin
- rinit := 'a'; (* abort *)
- debugwrite('wierd rinit packet');
- end
- end; (* rinit *)
-
- (* state table switcher for receiving packets *)
-
- begin (* recswok *)
- writescreen('Receiving');
- state := 'r'; (* initial state is send *)
- n := 0; (* set packet # *)
- numtry := 0; (* no tries yet *)
-
- while true do
- if state in ['d', 'f', 'r', 'c', 'a'] then
- case state of
- 'd': state := rdata;
- 'f': state := rfile;
- 'r': state := rinit;
- 'c': begin
- rec_ok := true;
- exit(recsw)
- end; (* case c *)
- 'a': begin
- rec_ok := false;
- exit(recsw)
- end (* case a *)
- end (* case *)
- else (* state not in legal states *)
- begin
- rec_ok := false;
- exit(recsw)
- end (* else *)
- end; (* recsw *)
-
-
- {>>>> PARSE.TEXT}
-
- segment function parse: statustype;
- (* NOTE: due to procedures at the end of this file, this must be the
- LAST segment declared *)
-
- type states = (start, fin, get_filename, get_set_parm, get_parity, get_on_off,
- get_char, get_show_parm, get_help_show, get_help_parm,
- exitstate);
-
- var status: statustype;
- word: vocab;
- state: states;
-
- procedure eatspaces(var s: string);
-
- var done: boolean;
- i: integer;
-
- begin
- done := (length(s) = 0);
- while not done do
- begin
- if s[1] = ' ' then
- begin
- i := length(s) - 1;
- s := copy(s,2,i);
- done := length(s) = 0
- end (* if *)
- else
- done := true
- end (* while *)
- end; (* eatspaces *)
-
- procedure isolate_word(var line, s: string);
-
- var i: integer;
- done: boolean;
-
- begin
- done := false;
- i := 1;
- s := copy(' ',0,0);
- while (i <= length(line)) and not done do
- begin
- if line[i] = ' ' then
- done := true
- else
- s := concat(s,copy(line,i,1));
- i := i + 1;
- end; (* while *)
- line := copy(line,i,length(line)-i+1);
- end; (* isolate_word *)
-
- function get_fn(var line, fn: string): boolean;
-
- var i, l: integer;
-
- begin
- get_fn := true;
- isolate_word(line, fn);
- l := length(fn);
- if (l < 1) then
- get_fn := false
- end; (* get_fn *)
-
- function getch(var ch: char): boolean;
-
- var s: string;
-
- begin
- isolate_word(line,s);
- if length(s) <> 1 then
- getch := false
- else
- begin
- ch := s[1];
- get_ch := true
- end (* else *)
- end; (* getch *)
-
-
- function get_sym(var word: vocab): statustype;
-
- var i: vocab;
- s: string;
- stat: statustype;
- done: boolean;
- matches: integer;
-
- begin
- eat_spaces(line);
- if length(line) = 0 then
- getsym := ateol
- else
- begin
- stat := null;
- done := false;
- isolate_word(line,s);
- i := allsym;
- matches := 0;
- repeat
- if (pos(s,vocablist[i]) = 1) and (i in expected) then
- begin
- matches := matches + 1;
- word := i
- end
- else if (s[1] < vocablist[i,1]) then
- done := true;
- if (i = spacesym) then
- done := true
- else
- i := succ(i)
- until (matches > 1) or done;
- if matches > 1 then
- stat := ambiguous
- else if (matches = 0) then
- stat := unrec;
- getsym := stat
- end (* else *)
- end; (* getsym *)
-
- begin
- state := start;
- parse := null;
- noun := nullsym;
- verb := nullsym;
- adj := nullsym;
- uppercase(line);
- repeat
- case state of
- start:
- begin
- expected := [consym, exitsym, helpsym, quitsym, recsym, sendsym,
- setsym, showsym];
- status := getsym(verb);
- if status = ateol then
- begin
- parse := null;
- exit(parse)
- end (* if *)
- else if (status <> unrec) and (status <> ambiguous) then
- case verb of
- consym: state := fin;
- exitsym, quitsym: state := fin;
- helpsym: state := get_help_parm;
- recsym: state := fin;
- sendsym: state := getfilename;
- setsym: state := get_set_parm;
- showsym: state := get_show_parm;
- end (* case *)
- end; (* case start *)
- fin:
- begin
- expected := [];
- status := getsym(verb);
- if status = ateol then
- begin
- parse := null;
- exit(parse)
- end (* if status *)
- else
- status := unconfirmed
- end; (* case fin *)
- getfilename:
- begin
- expected := [];
- if getfn(line,filename) then
- begin
- status := null;
- state := fin
- end (* if *)
- else
- status := fnexpected
- end; (* case get file name *)
- get_set_parm:
- begin
- expected := [paritysym, localsym, ibmsym, escsym,
- debugsym, filewarnsym];
- status := getsym(noun);
- if status = ateol then
- status := parm_expected
- else if (status <> unrec) and (status <> ambiguous) then
- case noun of
- paritysym: state := get_parity;
- localsym: state := get_on_off;
- ibmsym: state := get_on_off;
- escsym: state := getchar;
- debugsym: state := getonoff;
- filewarnsym: state := getonoff;
- end (* case *)
- end; (* case get_set_parm *)
- get_parity:
- begin
- expected := [marksym, spacesym, nonesym, evensym, oddsym];
- status := getsym(adj);
- if status = ateol then
- status := parm_expected
- else if (status <> unrec) and (status <> ambiguous) then
- state := fin
- end; (* case get_parity *)
- get_on_off:
- begin
- expected := [onsym, offsym];
- status := getsym(adj);
- if status = ateol then
- status := parm_expected
- else if (status <> unrec) and (status <> ambiguous) then
- state := fin
- end; (* get_on_off *)
- get_char:
- if getch(newescchar) then
- state := fin
- else
- status := ch_expected;
- get_show_parm:
- begin
- expected := [allsym, paritysym, localsym, ibmsym, escsym,
- debugsym, filewarnsym];
- status := getsym(noun);
- if status = ateol then
- status := parm_expected
- else if (status <> unrec) and (status <> ambiguous) then
- state := fin
- end; (* case get_show_parm *)
- get_help_show:
- begin
- expected := [paritysym, localsym, ibmsym, escsym,
- debugsym, filewarnsym];
- status := getsym(adj);
- if (status = at_eol) then
- begin
- status := null;
- state := fin
- end
- else if (status <> unrec) and (status <> ambiguous) then
- state := fin
- end; (* case get_help_show *)
- get_help_parm:
- begin
- expected := [consym, exitsym, helpsym, quitsym, recsym,
- sendsym, setsym, showsym];
- status := getsym(noun);
- if status = ateol then
- begin
- parse := null;
- exit(parse)
- end;
- if (status <> unrec) and (status <> ambiguous) then
- case noun of
- consym: state := fin;
- sendsym: state := fin;
- recsym: state := fin;
- setsym: state := get_help_show;
- showsym: state := fin;
- helpsym: state := fin;
- exitsym, quitsym: state := fin;
- end (* case *)
- end; (* case get_help_show *)
- end (* case *)
- until (status <> null);
- parse := status
- end; (* parse *)
-
- procedure initvocab;
-
- var i: integer;
-
- begin
- vocablist[allsym] := 'ALL';
- vocablist[consym] := 'CONNECT';
- vocablist[debugsym] := 'DEBUG';
- vocablist[escsym] := 'ESCAPE';
- vocablist[evensym] := 'EVEN';
- vocablist[exitsym] := 'EXIT';
- vocablist[filewarnsym] := 'FILE-WARNING';
- vocablist[helpsym] := 'HELP';
- vocablist[ibmsym] := 'IBM';
- vocablist[localsym] := 'LOCAL-ECHO';
- vocablist[marksym] := 'MARK';
- vocablist[nonesym] := 'NONE';
- vocablist[oddsym] := 'ODD';
- vocablist[offsym] := 'OFF';
- vocablist[onsym] := 'ON';
- vocablist[paritysym] := 'PARITY';
- vocablist[quitsym] := 'QUIT';
- vocablist[recsym] := 'RECEIVE';
- vocablist[sendsym] := 'SEND';
- vocablist[setsym] := 'SET';
- vocablist[showsym] := 'SHOW';
- vocablist[spacesym] := 'SPACE';
- end; (* initvocab *)
-
- procedure uppercase(*var s: string*);
-
- var i: integer;
-
- begin
- for i := 1 to length(s) do
- if s[i] in ['a'..'z'] then
- s[i] := chr(ord(s[i]) - ord('a') + ord('A'))
- end; (* uppercase *)
-
- {>>>>WDPROCS.TEXT}
- (* These drivers were adapted from routines written by Tim Shimeall
- for a PCNET implementation, based on information from Western
- Digital.
-
- On the Microengine, there are two RS232C Serial Ports. Port A is
- reserved for the system terminal. Port B is available for all other
- devices which may be desired to hang off a Microengine. In this code,
- it is assumed that Port B holds the modem.
- *)
- (* All functions are duplicated on ports A and B for simplicity *)
-
- PROCEDURE Init;
- BEGIN (* InitM *)
- PortB.DevAdd:= Channel0;
- PortA.DevAdd:= Channel1;
- WITH PortB.Serial^ DO BEGIN
- {The following two lines set the serial port to the following
- commands:
- Control1:
- 1 - Full Duplex Operation
- 0 - Break or Transmit NOT transparent
- 0 - Send 2 stop bits on Transmitted 8-bit data
- 0 - No echo of Recieved data
- 0 - Parity checking/generation OFF
- 1 - Reciever is enabled (chars in Rec. holding reg.)
- 1 - REQUEST TO SEND is enabled if CTS is low
- 1 - DTR is ON
- Control2:
- 0 - 8 bits
- 0 - 8 bits
- 0 - Asynchronous character mode
- 0 - even parity
- 0 - select reciever rate 1
- 0 - +
- 0 | - Clock select to rate 1 (32X)
- 1 - +
- }
- Control1:=135; {87 hex}
- Control2:=1;
- END;
- WITH PortA.Serial^ DO BEGIN
- Control1:=135;
- Control2:=1;
- END;
- END; (*InitM*)
-
- (*---------------------UART FLAG CHECKING-------------------------------*)
-
- function ISTARR(*:boolean *);
- (* ARR -- IS True Port A Receive Ready?
-
- This checks the UART status bit corresponding to Receive Data
- Available. If data is available a true result is returned.*)
- BEGIN
- ISTARR:=PortA.Serial^.StatSynDle.status[DataReceived];
- END;
-
- function ISTBRR(*:boolean*);
- (* BRR -- IS True Port B Receive Ready?*)
- BEGIN
- ISTBRR:=PortB.Serial^.StatSynDle.status[DataReceived];
- END;
-
- function ISTAOR(*:boolean*);
- (* AOR -- IS it True that data OverRun occurred?:0 istor<ditto>
-
- Immediately after RCVBT is called, ISTOR may be called to check for
- data overrun. This function isn't necessary, but it helps diagnose
- software that is losing data because it is too slow to receive data
- before that data starts getting shifted out of the way to make way for
- later data that has already started to arrive.
- *)
- BEGIN
- ISTAOR:=PortA.Serial^.StatSynDle.Status[OverError];
- END;
-
- function ISTBOR(*:boolean*);
- BEGIN
- ISTBOR:=PortB.Serial^.StatSynDle.Status[OverError];
- END;
-
- function ISTAFE(*:boolean *);
- (* FE -- IS it True that Framing-Error occurred?:0 istfe<ditto>
-
- Immediately after RCVBT is called, ISTFE may be called to check for
- framing error. This function isn't necessary, but it helps diagnose
- various errors such as phone-line-noise and wrong-speed-UART. Normally
- ISTOR will be called before ISTFE since data overrun is a more serious
- error than framing-error and thus pre-empts framing-error. The entire
- sequence is thus: ISTRR, RCVBT, ISTOR, ISTFE.
- *)
- BEGIN
- ISTAFE:=PortA.Serial^.StatSynDle.Status[FrameError];
- END;
-
- function ISTBFE(*:boolean*);
- BEGIN
- ISTBFE:=PortB.Serial^.StatSynDle.Status[FrameError];
- END;
-
- function ISTATR(*:boolean *);
- (* TR -- IS it True that Transmit is Ready?:0 isttr<used in FDX&SDWBT>
-
- ISTTR is analagous to ISTRR, it tells whether it's safe to transmit
- (rather than to receive) a byte of data. Internally it tells whether
- the previous byte has cleared the device so that the buffer is empty
- to accept another byte. In the device descripion it's usually called
- Transmit Buffer Empty. For instantaneous devices such as memory-mapped
- CRTs, this function will always return TRUE. For most other devices
- such as UARTs and ACIAs (connected directly to terminals, or to
- modems), ISTTR will return TRUE initially, then return FALSE as soon
- as a byte is sent to the device, and then return TRUE when actual
- transmission is done. For double-buffered devices it may only go FALSE
- only after two characters are sent to it, one of which is actually en
- route and the other of which is merely occupying the extra buffer.
- *)
- BEGIN
- ISTATR:=PortA.Serial^.StatSynDle.Status[RegEmpty];
- END;
-
- function ISTBTR(*:boolean*);
- BEGIN
- ISTBTR:=PortB.Serial^.StatSynDle.Status[RegEmpty];
- END;
-
- (*------------------Primitive character sending and receiving---------------*)
-
- function RCVABT(*:CHAR*) ;
- (* ReCeiVe ByTe of data from device:0 rcvbt<used in FDX and RCWBT>
-
- This is the function that is called after ISTRR returns true, to
- actually fetch the waiting data from the UART or ACIA into the
- computer, freeing the device to accept the next byte of data. These
- two functions, testing for data ready and actually fetching the data,
- are kept separate for two reasons: (1) they are separate hardware
- functions in most existing devices, ISTRR being a read of the status
- port with testing for a bit and RCVBT being a read of the data
- port, and (2) often they must be separate in the software, such as
- when it's necessary to verify both that data is available and there's
- a place to put it before fetching the data, such as in a terminal emulator.
-
- Note that calling RCVBT any time other than after getting a true
- result from ISTRR is invalid, yielding random garbage such as part of
- an incoming byte shifted. Note also that RCVBT fetches all 8 bits of
- the incoming byte of data, returning an 8-bit number with each bit in
- its normal position, for example the first-arrived bit is the 1 bit,
- then the 2 bit, etc., with the "parity" bit which is the last-arrived
- appearing simply as an 8th bit (hexadecimal value 80). No checking of
- parity is allowed, nor is stripping off of the parity bit. When only 7
- bits are desired, a higher-level function will strip off the parity bit.*)
- BEGIN
- RCVABT:=CHR(PortA.Serial^.SerData);
- END;
-
- function RCVBBT(*:CHAR*);
- BEGIN
- RCVBBT:=CHR(PortB.Serial^.SerData);
- END;
-
- procedure SNDABT(* (BT:CHAR)*);
- (* SeND ByTe of data:0 sndbt<used in FDX&SDWBT>
-
- After getting back a TRUE result from isttr, this function SNDBT is
- used to actually send the byte of data from the CPU to the device, so
- as to effect sending it out the I/O port (modem or local CRT). Note
- that any attempt to call SNDBT without first getting TRUE from isttr
- can result in clobbering previous data that is still in transit from
- the UART or ACIA bit by bit, causing both that previous byte and this
- new byte to be lost/garbaged. *)
- BEGIN (* SNDABT*)
- PortA.Serial^.SerData:=ORD(BT);
- END(*SNDABT*);
-
- procedure SNDBBT(* (BT:CHAR)*);
- (* SeND ByTe of data:0 sndbt<used in FDX&SDWBT>
-
- After getting back a TRUE result from isttr, this function SNDBT is
- used to actually send the byte of data from the CPU to the device, so
- as to effect sending it out the I/O port (modem or local CRT). Note
- that any attempt to call SNDBT without first getting TRUE from isttr
- can result in clobbering previous data that is still in transit from
- the UART or ACIA bit by bit, causing both that previous byte and this
- new byte to be lost/garbaged. *)
- BEGIN (* SNDBBT*)
- PortB.Serial^.SerData:=ORD(BT);
- END(*SNDBBT*);
-
- procedure finit;
- BEGIN
- PortB.Serial^.Control1:=0; {Turn off DTR, which causes modem to hang up}
- END;
-
-
- {>>>>UTILS.TEXT}
- function ready(p:port):boolean;
- begin
- ready:= ((p=terminal) and istarr) or ((p=modem) and istbrr);
- end;
-
- function pget(p:port):char;
- begin
- if p=terminal then pget:=rcvabt
- else pget:=rcvbbt;
- end;
-
- procedure read_str(*var p: port; var s: string*);
-
- (* acts like readln(s) but takes input from specified port *)
-
- var i: integer;
-
- begin
- i := 0;
- s := copy('',0,0);
- repeat
- repeat (* get a character *)
- until ready(p);
- ch:=pget(p);
- if (ord(ch) = backspace) then (* if it's a backspace then *)
- begin
- if (i > 0) then (* if not at beginning of line *)
- begin
- write(ch); (* go back a space on screen *)
- write(' '); (* erase char on screen *)
- write(ch); (* go back a space again *)
- i := i - 1; (* adjust string counter *)
- s := copy(s,1,i) (* adjust string *)
- end (* if *)
- end (* if *)
- else if (ord(ch) <> eoln_sym) then (* otherwise if not at eoln then *)
- begin
- write(ch); (* echo char on screen *)
- i := i + 1; (* inc string counter *)
- s := concat(s,' ');
- s[i] := ch; (* put char in string *)
- end; (* if *)
- until (ord(ch) = eoln_sym); (* if not eoln, get another char *)
- s := copy(s,1,i); (* correct string length *)
- writeln (* write a line on the screen *)
- end; (* read_str *)
-
- function read_ch(*p: port; var ch: char): boolean*);
-
- (* read a character from an input port *)
-
- begin
- if ready(p) then (* if a char there *)
- begin
- ch := pget(p); (* get the char *)
- read_ch := true; (* and return true *)
- end (* if *)
- else (* otherwise *)
- read_ch := false; (* return false *)
- end; (* read_ch *)
-
- function getch(*var r: char; p: port): boolean*);
-
- (* gets a character, strips parity, returns true if it got a char which *)
- (* isn't Kermit SOH, false if it gets SOH or nothing after maxtry *)
-
- const maxtry = 10000;
-
- var count: integer;
-
- begin
- count := 0;
- getch := false;
- repeat
- count := count + 1;
- until ready(p) or (count > maxtry); (* wait for a character *)
- if (count > maxtry) then (* if wait too long then *)
- exit(getch); (* get out of here *)
- r:=pget(p); (* get the character *)
- r := chr(aand(ord(r),127)); (* strip parity from char *)
- getch := (r <> chr(soh)); (* return true if not SOH *)
- end; (* getch *)
-
-
- function aand(*x,y: integer): integer*);
-
- (* arithmetic and--takes 2 integers and ands them, yeilding an integer *)
-
- var xrec, yrec, temp: int_bool_rec;
-
- begin
- xrec.i := x; (* put the two numbers in variant record *)
- yrec.i := y;
- temp.b := xrec.b * yrec.b; (* use as sets to 'and' them *)
- aand := temp.i (* return integer result *)
- end; (* aand *)
-
- function aor(*x,y: integer): integer*);
-
- (* arithmetic or *)
-
- var xrec, yrec, temp: int_bool_rec;
-
- begin
- xrec.i := x; (* put two numbers in variant record *)
- yrec.i := y;
- temp.b := xrec.b + yrec.b; (* use as sets to 'or' them *)
- aor := temp.i (* return integer result *)
- end; (* aor *)
-
- function xor(*x,y: integer): integer*);
-
- (* exclisive or *)
-
- var xrec, yrec, temp: int_bool_rec;
-
- begin
- xrec.i := x; (* put two numbers in variant record *)
- yrec.i := y;
- (* use as sets to 'xor' them *)
- temp.b := (xrec.b - yrec.b) + (yrec.b - xrec.b);
- xor := temp.i (* return integer result *)
- end; (* xor *)
-
- procedure error(*p: packettype; len: integer*);
-
- (* writes error message sent by remote host *)
-
- var i: integer;
-
- begin
- gotoxy(0,errorline);
- for i := 0 to len-1 do
- write(p[i]);
- gotoxy(0,promptline);
- end; (* error *)
-
- procedure io_error(*i: integer*);
-
- begin
- gotoxy(0,errorline);
- write(chr(27),'K'); (* erase to end of line *)
- case i of
- 0: writeln('No error');
- 1: writeln('Bad Block, Parity error (CRC)');
- 2: writeln('Bad Unit Number');
- 3: writeln('Bad Mode, Illegal operation');
- 4: writeln('Undefined hardware error');
- 5: writeln('Lost unit, Unit is no longer on-line');
- 6: writeln('Lost file, File is no longer in directory');
- 7: writeln('Bad Title, Illegal file name');
- 8: writeln('No room, insufficient space');
- 9: writeln('No unit, No such volume on line');
- 10: writeln('No file, No such file on volume');
- 11: writeln('Duplicate file');
- 12: writeln('Not closed, attempt to open an open file');
- 13: writeln('Not open, attempt to close a closed file');
- 14: writeln('Bad format, error in reading real or integer');
- 15: writeln('Ring buffer overflow')
- end; (* case *)
- gotoxy(0,promptline)
- end; (* io_error *)
-
- procedure debugwrite(*s: string*);
-
- (* writes a debugging message *)
- var i: integer;
-
- begin
- if debug then
- begin
- gotoxy(0,debugline+debnext);
- debnext:=(debnext+1) mod debug_max;
- write(chr(27),'K'); (* erase to end of line *)
- write(s); (* write debugging message *)
- end (* if debug *)
- end; (* debugwrite *)
-
- procedure debugint(*s: string; i: integer*);
-
- (* write a debugging message and an integer *)
-
- begin
- if debug then
- begin
- debugwrite(s);
- write(i)
- end (* if debug *)
- end; (* debugint *)
-
- procedure writescreen(*s: string*);
-
- (* sets up the screen for receiving or sending files *)
-
- begin
- write(chr(esc),'E'{clearscreen});
- gotoxy(0,titleline);
- write(' Kermit UCSD p-system');
- gotoxy(statuspos,statusline);
- write(s);
- gotoxy(0,packetline);
- write('Number of Packets: ');
- gotoxy(0,retryline);
- write('Number of Tries: ');
- gotoxy(0,fileline);
- write('File Name: ');
- end; (* writescreen *)
-
- procedure refresh_screen(*numtry, num: integer*);
-
- (* keeps track of packet count on screen *)
-
- begin
- gotoxy(retrypos,retryline);
- write(numtry: 5);
- gotoxy(packetpos,packetline);
- write(num: 5)
- end; (* refresh_screen *)
-
- function min(*x,y: integer): integer*);
-
- (* returns smaller of two integers *)
-
- begin
- if x < y then
- min := x
- else
- min := y
- end; (* min *)
-
- function tochar(*ch: char): char*);
-
- (* tochar converts a control character to a printable one by adding space *)
-
- begin
- tochar := chr(ord(ch) + ord(' '))
- end; (* tochar *)
-
- function unchar(*ch: char): char*);
-
- (* unchar undoes tochar *)
-
- begin
- unchar := chr(ord(ch) - ord(' '))
- end; (* unchar *)
-
- function ctl(*ch: char): char*);
-
- (* ctl toggles control bit: ^A becomes A, A becomes ^A *)
-
- begin
- ctl := chr(xor(ord(ch),64))
- end; (* ctl *)
-
- procedure echo(ch: char);
-
- (* echos a character on the screen *)
-
- begin
- ch := chr(aand(ord(ch),127)); (* mask off parity bit *)
- repeat until istatr;
- sndabt(ch)
- end; (* echo *)
-
- {>>>>RSUTILS.TEXT}
- function getfil(*filename: string): boolean*);
-
- (* opens a file for writing *)
-
- begin
- (*$I-*) (* turn i/o checking off *)
- rewrite(f,filename);
- (*$I-*) (* turn i/o checking on *)
- getfil := (ioresult = 0)
- end; (* getfil *)
-
- procedure bufemp(*buffer: packettype; var f: text; len: integer*);
-
- (* empties a packet into a file *)
-
- var i,ls: integer;
- r: char;
- s: string;
-
- begin
- s := copy('',0,0);
- ls := 0;
- i := 0;
- while i < len do
- begin
- r := buffer[i]; (* get a character *)
- if (r = myquote) then (* if character is control quote *)
- begin
- i := i + 1; (* skip over quote and *)
- r := buffer[i]; (* get quoted character *)
- if (aand(ord(r),127) <> ord(myquote)) then
- r := ctl(r); (* controllify it *)
- end; (* if *)
- if (ord(r) = cr) then (* else if a carriage return then *)
- begin
- i := i + 3; (* skip over that and line feed *)
- (*$I-*) (* turn i/o checking off *)
- writeln(f,s); (* and write out line to file *)
- s := copy('',0,0); (* empty the string var *)
- ls := 0;
- if (io_result <> 0) then (* if io_error *)
- begin
- io_error(ioresult); (* tell them and *)
- state := 'a'; (* abort *)
- end (* if *)
- end
- (*$I+*) (* turn i/o checking back on *)
- else (* else, is a regular char, so *)
- begin
- r:= chr(aand(ord(r),127)); (* mask off parity bit *)
- s := concat(s,' '); (* and add character to out string *)
- ls := ls + 1;
- s[ls] := r;
- i := i + 1 (* increase buffer pointer *)
- end; (* else *)
- end; (* while *) (* and get another char *)
- (*$I-*) (* turn i/o checking off *)
- write(f,s); (* and write out line to file *)
- if (io_result <> 0) then (* if io_error *)
- begin
- io_error(ioresult); (* tell them and *)
- state := 'a'; (* abort *)
- end (* if *)
- (*$I+*) (* turn i/o checking back on *)
- end; (* bufemp *)
-
- function bufill(*var buffer: packettype): integer*);
-
- (* fill a packet with data from a file...manages a 2 block buffer *)
-
- var i, j, k, t7, count: integer;
- r: char;
-
- begin
- i := 0;
- (* while file has some data & packet has some room we'll keep going *)
- while ((bufpos <= bufend) or (not eof(oldf))) and (i < spsiz-9) do
- begin
- (* if we need more data from disk then *)
- if (bufpos > bufend) and (not eof(oldf)) then
- begin
- (* read a couple of blocks *)
- bufend := blockread(oldf,filebuf[1],2) * blksize;
- (* and adjust buffer pointer *)
- bufpos := 1
- end; (* if *)
- if (bufpos <= bufend) then (* if we're within buffer bounds *)
- begin
- r := filebuf[bufpos]; (* get a character *)
- bufpos := bufpos + 1; (* increase buffer pointer *)
- if (ord(r) = dle) then (* if it's space compression char, *)
- begin
- count := ord(unchar(filebuf[bufpos])); (* get # of spaces *)
- bufpos := bufpos + 1; (* read past # *)
- r := ' '; (* and make current char a space *)
- end (* else if *)
- else (* otherwise, it's just a char *)
- count := 1; (* so only 1 copy of it *)
- if (r in ctlset) then (* if a control char *)
- begin
- if (ord(r) = cr) then (* if a carriage return *)
- begin
- buffer[i] := quote; (* put (quoted) CR in buffer *)
- i := i + 1;
- buffer[i] := ctl(chr(cr));
- i := i + 1;
- r := chr(lf); (* and we'll stick a LF after *)
- end; (* if *)
- if r <> chr(0) then (* if not a NUL then *)
- begin
- buffer[i] := quote; (* put the quote in buffer *)
- i := i + 1;
- if r <> quote then
- r := ctl(r); (* and un-controllify char *)
- end (* if *)
- end; (* if *)
- end; (* if *)
- j := 1;
- while (j <= count) and (i <= spsiz - 5) do
- begin (* put all the chars in buffer *)
- if (ord(r) <> 0) then (* so long as not a NUL *)
- begin
- buffer[i] := r;
- i := i + 1;
- end (* if *)
- else (* is a NUL so *)
- if (bufpos > blksize) then (* skip to end of block *)
- bufpos := bufend + 1 (* since rest will be NULs *)
- else
- bufpos := blksize + 1;
- j := j + 1
- end; (* while *)
- end; (* while *)
- if (i = 0) then (* if we're at end of file, *)
- bufill := (at_eof) (* indicate it *)
- else (* else *)
- begin
- if (j <= count) then (* if didn't all fit in packet *)
- begin
- bufpos := bufpos - 2; (* put buf pointer at DLE *)
- (* and update compress count *)
- filebuf[bufpos + 1] := tochar(chr(count-j+1));
- end; (* if *)
- bufill := i (* return # of chars in packet *)
- end; (* else *)
- end; (* bufill *)
-
- procedure spar(*var packet: packettype*);
-
- (* fills data array with my send-init parameters *)
-
- begin
- packet[0] := tochar(chr(maxpack)); (* biggest packet i can receive *)
- packet[1] := tochar(chr(mytime)); (* when i want to be timed out *)
- packet[2] := tochar(chr(mypad)); (* how much padding i need *)
- packet[3] := ctl(chr(mypchar)); (* padding char i want *)
- packet[4] := tochar(chr(myeol)); (* end of line character i want *)
- packet[5] := myquote; (* control-quote char i want *)
- packet[6] := 'N'; (* I won't do 8-bit quoting *)
- end; (* spar *)
-
- procedure rpar(*var packet: packettype*);
-
- (* gets their init params *)
- var s:string;
- begin
- s:='rpar:spsize:## timint:## pad:## padchar:### eol:### quote:###';
- spsiz := ord(unchar(packet[0])); (* max send packet size *)
- s[13]:=chr(ord('0')+(spsiz div 10));
- s[14]:=chr(ord('0')+(spsiz mod 10));
- timint := ord(unchar(packet[1])); (* when i should time out *)
- s[23]:=chr(ord('0')+(timint div 10));
- s[24]:=chr(ord('0')+(timint mod 10));
- pad := ord(unchar(packet[2])); (* number of pads to send *)
- s[30]:=chr(ord('0')+(pad div 10));
- s[31]:=chr(ord('0')+(pad mod 10));
- padchar := ctl(packet[3]); (* padding char to send *)
- s[41]:=chr(ord('0')+(ord(padchar) div 100));
- s[42]:=chr(ord('0')+((ord(padchar) mod 100) div 10));
- s[43]:=chr(ord('0')+(ord(padchar) mod 10));
- eol := unchar(packet[4]); (* eol char i must send *)
- s[49]:=chr(ord('0')+(ord(eol) div 100));
- s[50]:=chr(ord('0')+((ord(eol) mod 100) div 10));
- s[51]:=chr(ord('0')+(ord(eol) mod 10));
- quote := packet[5]; (* incoming data quote char *)
- s[59]:=chr(ord('0')+(ord(quote) div 100));
- s[60]:=chr(ord('0')+((ord(quote) mod 100) div 10));
- s[61]:=chr(ord('0')+(ord(quote) mod 10));
- debugwrite(s);
- end; (* rpar *)
-
- procedure packetwrite(*p: packettype; len: integer*);
-
- (* writes out all of a packet for debugging purposes *)
-
- var i: integer;
- s: string;
- begin
- s:='length:## Sequence:## Type: #';
- if p[0]=chr(soh) then s:=concat('SOH ',s);
- s[8]:=chr(ord('0')+(ord(p[1]) div 10));
- s[9]:=chr(ord('0')+(ord(p[1]) mod 10));
- s[20]:=chr(ord('0')+(ord(p[2]) div 10));
- s[21]:=chr(ord('0')+(ord(p[2]) mod 10));
- s[length(s)]:=p[3];
- debugwrite(s);
- gotoxy(0,debugline+debnext);
- debnext:=(debnext+1) mod debug_max;
- for i := 4 to len+3 do
- begin
- if i = 84 then
- begin
- gotoxy(0,debugline+debnext);
- debnext:=(debnext+1) mod debug_max;
- write(chr(27),'K');
- end; (* if *)
- write(p[i])
- end; (* for *)
- end; (* packetwrite *)
-
- procedure spack(*ptype: char; num: integer; len: integer; data: packettype*);
-
- (* send a packet *)
-
- const maxtry = 10000;
-
- var bufp, i, count: integer;
- chksum: char;
- buffer: packettype;
- ch: char;
-
- begin
- if ibm and (state <> 's') then (* if ibm and not SINIT then *)
- begin
- count := 0;
- repeat (* wait for an xon *)
- repeat
- count := count + 1
- until (readch(modem,ch)) or (count > maxtry );
- until (ch = xon) or (count > maxtry);
- if count > maxtry then (* if wait too long then *)
- begin
- exit(spack) (* get out *)
- end; (* if *)
- end; (* if *)
-
- bufp := 0;
- for i := 1 to pad do begin
- while not istbtr do ;
- sndbbt(padchar); (* write out any padding chars *)
- end;
- buffer[bufp] := chr(soh); (* packet sync character *)
- bufp := bufp + 1;
- chksum := tochar(chr(len + 3)); (* init chksum *)
- buffer[bufp] := tochar(chr(len + 3)); (* character count *)
- bufp := bufp + 1;
- chksum := chr(ord(chksum) + ord(tochar(chr(num))));
- buffer[bufp] := tochar(chr(num));
- bufp := bufp + 1;
- chksum := chr(ord(chksum) + ord(ptype));
- buffer[bufp] := ptype; (* packet type *)
- bufp := bufp + 1;
-
- for i := 0 to len - 1 do (* loop through data chars *)
- begin
- buffer[bufp] := data[i]; (* store char *)
- bufp := bufp + 1;
- chksum := chr(ord(chksum) + ord(data[i]))
- end; (* for i *)
- (* compute final chksum *)
- chksum := chr(aand(ord(chksum) + (aand(ord(chksum),192) div 64), 63));
- buffer[bufp] := tochar(chksum);
- bufp := bufp + 1;
- buffer[bufp] := eol;
-
- if (parity <> nopar) then
- for i := 0 to bufp do (* set correct parity on buffer *)
- buffer[i] := parity_array[buffer[i]];
-
- for i:=0 to bufp do begin
- while not istbtr do;
- sndbbt(buffer[i]); (* send the packet out *)
- end;
-
- debugwrite('sending');
- if debug then
- packetwrite(buffer,len);
- end; (* spack *)
-
- function getsoh(*p: port): boolean*);
-
- (* reads characters until it finds an SOH; returns false if has to read more *)
- (* than maxtry chars *)
-
- const maxtry = 10000; (* allows about 1 second of trying *)
-
- var ch: char;
- seconds,count: integer;
-
- begin
- count := 0;
- seconds:=0;
- get_soh := true;
- repeat
- repeat
- count := count + 1;
- if count>maxtry then begin
- seconds:=seconds+1;
- count:=0;
- end;
- until ready(p) or (seconds > timint); (* wait for a character *)
- if (seconds > timint) then
- begin
- get_soh := false;
- exit(get_soh);
- end;
- ch := pget(p); (* get the character *)
- ch := chr(aand(ord(ch),127)); (* strip parity of char *)
- until (ch = chr(SOH)) (* if not SOH, get more *)
- end; (* getsoh *)
-
- (*$G+*) (* turn on goto option...need it for next routine *)
-
- function rpack(*var len, num: integer; var data: packettype): char*);
-
- (* read a packet *)
-
- label 1; (* used to emulate C's CONTINUE statement *)
-
- const maxtry = 10000; (* allows for about 1 second of checking *)
-
- var seconds, count, i, ichksum: integer;
- chksum, ptype: char;
- r: char;
-
- begin
- count := 0;
- seconds := 0;
-
- if not getsoh(modem) and (state<>'r') then (*if don't get synch char then *)
- begin
- rpack := 'N'; (* treat as a NAK *)
- num := n mod 64;
- exit(rpack) (* and get out of here *)
- end;
-
- 1: count := count + 1;
- if (count>maxtry)and(state<>'r') then (* end of one second *)
- if seconds<timint then begin (* and aren't waiting for init *)
- count:=0;
- seconds:=seconds+1;
- end
- else begin (* if we've tried too many times *)
- rpack := 'N'; (* treat as NAK *)
- exit(rpack) (* and get out of here *)
- end; (* if *)
-
- if not getch(r,modem) then (* get a char and *)
- goto 1; (* resynch if soh *)
-
- ichksum := ord(r); (* start checksum *)
- len := ord(unchar(r)) - 3; (* character count *)
-
- if not getch(r,modem) then (* get a char and *)
- goto 1; (* resynch if soh *)
- ichksum := ichksum + ord(r);
- num := ord(unchar(r)); (* packet number *)
-
- if not getch(r,modem) then (* get a char and *)
- goto 1; (* resynch if soh *)
- ichksum := ichksum + ord(r);
- ptype := r; (* packet type *)
-
- for i := 0 to len-1 do (* get any data *)
- begin
- if not getch(r,modem) then (* get a char and *)
- goto 1; (* resynch if soh *)
- ichksum := ichksum + ord(r);
- data[i] := r;
- end; (* for i *)
- data[len] := chr(0); (* mark end of data *)
-
- if not getch(r,modem) then (* get a char and *)
- goto 1; (* resynch if soh *)
-
- (* compute final checksum *)
- chksum := chr(aand(ichksum + (aand(ichksum,192) div 64), 63));
-
- if (chksum <> unchar(r)) then (* if checksum bad *)
- rpack := chr(0) (* return 'false' indicator *)
- else (* else *)
- rpack := ptype; (* return packet type *)
-
- if debug then
- begin
- gotoxy(0,debugline+debnext);
- debnext:= (debnext+1) mod debug_max;
- write('rpack: len:',len,' num:',num,' ptype:',ptype);
- end; (* if *)
- end; (* rpack *)
-
- (*$G-*) (* turn off goto option...don't need it anymore *)
-